﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NUnit.Framework;
using Downplay.Alchemy.Dynamic;
using System.Collections;

namespace Downplay.Alchemy.Tests.Dynamics {

    [TestFixture]
    public class TypeTests {

        [Test]
        public void EmptyInstanceHasNullType() {
            dynamic stuff = new Stuff();
            Assert.That((string)stuff.Stereotype(),Is.EqualTo("Null"));
            Assert.That((string)stuff.Supertype(), Is.EqualTo("Null"));
            Assert.That((stuff == null), Is.EqualTo(true));
            Assert.That(stuff.Foo == null, Is.EqualTo(true));
        }
        [Test]
        public void InitializeEmpty() {
            dynamic stuff = new Stuff();
            object foo = new Object();
            Assert.That((stuff == null), Is.EqualTo(true));
            Assert.That((stuff != null), Is.EqualTo(false));
            Assert.That((stuff == foo), Is.EqualTo(false));
            Assert.That((stuff != foo), Is.EqualTo(true));
        }

        [Test]
        public void InitializeWithString() {
            dynamic stuff = new Stuff("Foo");
            Assert.That((string)stuff.Supertype(), Is.EqualTo("Value"));
            Assert.That((string)stuff.Stereotype(), Is.EqualTo("String"));
            Assert.That((string)stuff, Is.EqualTo("Foo"));
            Assert.That((stuff == null), Is.EqualTo(false));
            Assert.That((stuff != null), Is.EqualTo(true));
            Assert.That((stuff == "Foo"), Is.EqualTo(true));
            Assert.That((stuff != "Foo"), Is.EqualTo(false));
        }

        [Test]
        public void InitializeWithStringArray() {
            dynamic stuff = new Stuff(new[] { "Foo", "Bar" });
            Assert.That(stuff.Supertype(), Is.EqualTo("Array"));
            IEnumerable<dynamic> enumerable = stuff;
            Assert.That(enumerable, Is.Not.Null);
            var strings = enumerable.Select(d=>(string)d);
            Assert.That(String.Join(",", strings), Is.EqualTo("Foo,Bar"));
            Assert.That(strings.First(), Is.EqualTo("Foo"));
            Assert.That(strings.Last(), Is.EqualTo("Bar"));
            Assert.That((int)stuff.Count(), Is.EqualTo(2));
        }
        [Test]
        public void InitializeWithIntArray() {
            dynamic stuff = new Stuff(new[] { 5,45,28,12314,23,6 });
            Assert.That(stuff.Supertype(), Is.EqualTo("Array"));
            Assert.That(stuff.Count(), Is.EqualTo(6));
            IEnumerable<dynamic> enumerable = stuff;
            Assert.That(enumerable, Is.Not.Null);
            var ints = enumerable.Select(d => (int)d);
            Assert.That(ints.First(), Is.EqualTo(5));
            Assert.That(ints.Last(), Is.EqualTo(6));
        }

        [Test]
        public void InitializeWithEmptyAnonymousType() {
            dynamic stuff = new Stuff(new { });
            Assert.That((string)stuff.Supertype(), Is.EqualTo("Object"));
            Assert.That((string)stuff.Stereotype(), Is.EqualTo("Anonymous"));
            Assert.That((stuff == null), Is.EqualTo(false));
            Assert.That((stuff != null), Is.EqualTo(true));
        }

        [Test]
        public void CanInitializeWithAnonymousType() {
            dynamic stuff = new Stuff(new { Foo = "Bar" });
            Assert.That((string)stuff.Supertype(), Is.EqualTo("Object"));
            Assert.That((string)stuff.Stereotype(), Is.EqualTo("Anonymous"));
            Assert.That((string)stuff.Foo, Is.EqualTo("Bar"));
            Assert.That((stuff == null), Is.EqualTo(false));
            Assert.That((stuff != null), Is.EqualTo(true));
        }

        [Test]
        public void InitializeWithConcreteClass() {
            dynamic stuff = new Stuff(new FooBar());
            Assert.That((string)stuff.Supertype(), Is.EqualTo("Object"));
            Assert.That((string)stuff.Stereotype(), Is.EqualTo("FooBar"));
            Assert.That((string)stuff.Foo, Is.EqualTo("Bar"));
            Assert.That((stuff == null), Is.EqualTo(false));
            Assert.That((stuff != null), Is.EqualTo(true));
        }

        [Test]
        public void InitializeWithDictionary() {
            dynamic stuff = new Stuff(new Dictionary<string, object>{
                { "Foo", "Bar" },
                { "One", 1 },
                { "Silly", new string[] { "Stuff", "Is", "Fun" } }
            });
            Assert.That((string)stuff.Foo, Is.EqualTo("Bar"));
            Assert.That((int)stuff.One, Is.EqualTo(1));
            Assert.That(stuff.Silly.Supertype(), Is.EqualTo("Array"));
            Assert.That(stuff.Silly.Count(), Is.EqualTo(3));
        }

        public class FooBar {
            public string Foo { get { return "Bar"; } }
        }

        [Test]
        public void ChainStereotypeAndSupertypeSetters() {
            dynamic stuff = new Stuff();
            stuff.Stereotype("Foo").Supertype("Bar");
            Assert.That((string)stuff.Stereotype(), Is.EqualTo("Foo"));
            Assert.That((string)stuff.Supertype(), Is.EqualTo("Bar"));
            stuff.Supertype("Foo").Stereotype("Bar");
            Assert.That((string)stuff.Stereotype(), Is.EqualTo("Bar"));
            Assert.That((string)stuff.Supertype(), Is.EqualTo("Foo"));

        }
        [Test]
        public void ArrayAddMethodsCreateArrayType() {
            dynamic stuff = new Stuff();
            stuff.Add("Foo");
            Assert.That((string)stuff.Supertype(), Is.EqualTo("Array"));
            Assert.That((int)stuff.Count(), Is.EqualTo(1));

            stuff = new Stuff();
            stuff.AddRange(new[] { "Foo", "Bar" });
            Assert.That((string)stuff.Supertype(), Is.EqualTo("Array"));
            Assert.That((int)stuff.Count(), Is.EqualTo(2));
        }

        [Test]
        public void StringStoredOnIndexCreatesValueType() {
            dynamic stuff = new Stuff();
            stuff["Foo"] = "Bar";
            Assert.That((string)(stuff.Supertype()), Is.EqualTo("Object"));
            Assert.That((string)stuff.Stereotype(), Is.EqualTo("Stuff"));
            Assert.That((string)stuff.Foo, Is.EqualTo("Bar"));
            Assert.That((stuff.Foo == "Bar"), Is.EqualTo(true));

            Assert.That((string)(stuff.Foo.Supertype()), Is.EqualTo("Value"));
            Assert.That((string)(stuff.Foo.Stereotype()), Is.EqualTo("String"));
            Assert.That((stuff == null), Is.EqualTo(false));
        }
        [Test]
        public void StringStoredOnPropertyCreatesValueType() {
            dynamic stuff = new Stuff();
            stuff.Foo = "Bar";
            Assert.That((string)(stuff.Supertype()), Is.EqualTo("Object"));
            Assert.That((string)stuff.Stereotype(), Is.EqualTo("Stuff"));
            Assert.That((string)stuff.Foo, Is.EqualTo("Bar"));
            Assert.That((stuff.Foo == "Bar"), Is.EqualTo(true));

            Assert.That((string)(stuff.Foo.Supertype()), Is.EqualTo("Value"));
            Assert.That((string)(stuff.Foo.Stereotype()), Is.EqualTo("String"));
            Assert.That((stuff == null), Is.EqualTo(false));
        }
        [Test]
        public void NullStoredOnPropertyClearsAll() {
            dynamic stuff = new Stuff();
            stuff.Foo = new { Bar = new { Car = "Zar" } };
            Assert.That(stuff.Foo == null, Is.EqualTo(false));
            Assert.That(stuff.Foo.Bar == null, Is.EqualTo(false));
            Assert.That(stuff.Foo.Bar.Car == null, Is.EqualTo(false));
            string nil = null;
            stuff.Foo = nil;
            Assert.That(stuff.Foo == null, Is.EqualTo(true));
            Assert.That(stuff.Foo.Bar == null, Is.EqualTo(true));
            Assert.That(stuff.Foo.Bar.Car == null, Is.EqualTo(true));
        }
        [Test]
        public void SetValuePropertyFromOtherStuff() {
            dynamic stuff1 = new Stuff();
            stuff1.Foo = "Bar";

            dynamic stuff2 = new Stuff();
            stuff2.Car = stuff1.Foo;

            Assert.That(stuff2.Car.Supertype(), Is.EqualTo("Value"));
            Assert.That(stuff2.Car.Stereotype(),Is.EqualTo("String"));
            Assert.That(stuff2.Car.Get(), Is.EqualTo("Bar"));
            Assert.That(stuff2.Car, Is.Not.EqualTo(stuff2.Foo), "Should be a new Stuff instance");

        }

    }
}
